package fun.mashuai.generator.plugins;

import fun.mashuai.generator.util.StringUtil;
import org.mybatis.generator.api.GeneratedXmlFile;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.JavaElement;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.api.dom.xml.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.*;


/**
 * @className EntityPlugin.java
 * @description 自定义注释生成
 * @author 马帅
 * @date 2018-08-22 22:35
 * @version 1.0
 */
public class EntityPlugin extends PluginAdapter {

    private static Logger logger = LoggerFactory.getLogger(EntityPlugin.class);

    private final static String ROOT_CLASS = "rootClass";

    private Boolean hasRootClass(){
        return this.getProperties().get("rootClass") != null && this.getProperties().get("rootClass") != "";
    }

    @Override
    public boolean validate(List<String> list) {
        return true;
    }

    /**
     * 类名注释
     * 类名：获取到的表名转驼峰命名方式
     */
    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        if(hasRootClass()){
            topLevelClass.addImportedType(this.getProperties().get("rootClass").toString());
        }

        topLevelClass.getJavaDocLines().clear();

        topLevelClass.addJavaDocLine("/**\n" +
                " * @className " + StringUtil.upperFirst(StringUtil.toCamelCase(introspectedTable.getFullyQualifiedTable().toString())) + ".java\n" +
                " * @description 数据表: " + introspectedTable.getFullyQualifiedTable() + " 实体类\n" +
                " * @author 马帅\n" +
                " * @date " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date()) + "\n" +
                " * @version 1.0\n" +
                " */");
        return true;
    }

    @Override
    public boolean modelFieldGenerated(Field field, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, ModelClassType modelClassType) {
        this.comment(field, introspectedTable, introspectedColumn);
        return true;
    }

    @Override
    public boolean modelGetterMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, ModelClassType modelClassType) {
        return true;
    }

    @Override
    public boolean modelSetterMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, ModelClassType modelClassType) {
        return true;
    }


    private void comment(JavaElement element, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
        element.getJavaDocLines().clear();
        element.addJavaDocLine("/**");
        String remark = introspectedColumn.getRemarks();
        if (remark != null && remark.length() > 1) {
            element.addJavaDocLine(" * " + remark);
            element.addJavaDocLine(" *");
        }

        element.addJavaDocLine(" * 字段:    " + introspectedColumn.getActualColumnName());
        element.addJavaDocLine(" * 类型:    " + introspectedColumn.getJdbcTypeName() + "(" +introspectedColumn.getLength()+ ")");
        element.addJavaDocLine(" * 允许空:  " + introspectedColumn.isNullable());
        element.addJavaDocLine(" * 默认值:  " + introspectedColumn.getDefaultValue());

        element.addJavaDocLine(" */");
    }

    @Override
    public boolean sqlMapResultMapWithoutBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        this.commentResultMap(element, introspectedTable);
        return true;
    }

    @Override
    public boolean sqlMapResultMapWithBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        this.commentResultMap(element, introspectedTable);
        return true;
    }


    void commentResultMap(XmlElement element, IntrospectedTable introspectedTable) {
        List<Element> es = element.getElements();
        if (!es.isEmpty()) {
            String alias = introspectedTable.getTableConfiguration().getAlias();
            int aliasLen = -1;
            if (alias != null) {
                aliasLen = alias.length() + 1;
            }

            Iterator<Element> it = es.iterator();
            HashMap map = new HashMap();

            while(true) {
                while(it.hasNext()) {
                    Element e = (Element)it.next();
                    if (e instanceof TextElement) {
                        it.remove();
                    } else {
                        XmlElement el = (XmlElement)e;
                        List<Attribute> as = el.getAttributes();
                        if (!as.isEmpty()) {
                            String col = null;
                            Iterator i$ = as.iterator();

                            while(i$.hasNext()) {
                                Attribute a = (Attribute)i$.next();
                                if (a.getName().equalsIgnoreCase("column")) {
                                    col = a.getValue();
                                    break;
                                }
                            }

                            if (col != null) {
                                if (aliasLen > 0) {
                                    col = col.substring(aliasLen);
                                }

                                IntrospectedColumn ic = introspectedTable.getColumn(col);
                                if (ic != null) {
                                    StringBuilder sb = new StringBuilder();
                                    if (ic.getRemarks() != null && ic.getRemarks().length() > 1) {
                                        sb.append("<!-- ");
                                        sb.append(ic.getRemarks());
                                        sb.append(" -->");
                                        map.put(el, new TextElement(sb.toString()));
                                    }
                                }
                            }
                        }
                    }
                }

                if (map.isEmpty()) {
                    return;
                }

                Set<Element> set = map.keySet();
                Iterator i$ = set.iterator();

                while(i$.hasNext()) {
                    Element e = (Element)i$.next();
                    int id = es.indexOf(e);
                    es.add(id, (Element) map.get(e));
                    es.add(id, new TextElement(""));
                }

                return;
            }
        }
    }

    @Override
    public boolean sqlMapInsertElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        this.removeAttribute(element.getAttributes(), "parameterType");
        return true;
    }

    @Override
    public boolean sqlMapInsertSelectiveElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        this.removeAttribute(element.getAttributes(), "parameterType");
        return true;
    }

    @Override
    public boolean sqlMapUpdateByPrimaryKeySelectiveElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        this.removeAttribute(element.getAttributes(), "parameterType");
        return true;
    }

    @Override
    public boolean sqlMapUpdateByPrimaryKeyWithBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        this.removeAttribute(element.getAttributes(), "parameterType");
        return true;
    }

    @Override
    public boolean sqlMapUpdateByPrimaryKeyWithoutBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        this.removeAttribute(element.getAttributes(), "parameterType");
        return true;
    }


    private void removeAttribute(List<Attribute> as, String name) {
        if (!as.isEmpty()) {
            Iterator it = as.iterator();

            Attribute attr;
            do {
                if (!it.hasNext()) {
                    return;
                }

                attr = (Attribute)it.next();
            } while(!attr.getName().equalsIgnoreCase(name));

            it.remove();
        }
    }

    @Override
    public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {

        logger.info("\n" +
                "" +
                "" +
                "                                _oo0oo_\n" +
                "                               o8888888o\n" +
                "                               88\" . \"88\n" +
                "                               (| -_- |)\n" +
                "                               0\\  =  /0\n" +
                "                             ___/`---'\\___\n" +
                "                           .' \\\\|     |// '.\n" +
                "                          / \\\\|||  :  |||// \\\n" +
                "                         / _||||| -:- |||||- \\\n" +
                "                        |   | \\\\\\  -  /// |   |\n" +
                "                        | \\_|  ''\\---/''  |_/ |\n" +
                "                        \\  .-\\__  '-'  ___/-. /\n" +
                "                      ___'. .'  /--.--\\  `. .'___\n" +
                "                   .\"\" '<  `.___\\_<|>_/___.' >' \"\".\n" +
                "                  | | :  `- \\`.;`\\ _ /`;.`/ - ` : | |\n" +
                "                  \\  \\ `_.   \\_ __\\ /__ _/   .-` /  /\n" +
                "              =====`-.____`.___ \\_____/___.-`___.-'=====\n" +
                "                              `=---='\n" +
                "\n" +
                "     卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐\n" +
                "   卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐\n" +
                " 卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐卐\n" +
                "\n" +
                "                      佛祖保佑         永无BUG\n" +
                "\n" +
                "\n" +
                "");

        logger.info("------------------------------------------------------------------------\n");
        logger.info("<!-- 非常感谢您使用 《Mybatis Generator + Lombok》 -->");
        logger.info("<!-- 有问题请联系微信或手机：15010589578 -->");
        logger.info("<!-- 生成时间: " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date()) + " -->\n");

        return true;
    }

    /**
     * 重新生成mapping文件时，不会覆盖原文件
     * mybatis 在IntrospectedTableMyBatis3Impl.getGeneratedXmlFiles方法中，isMergeable值被写死为true了。
     * 此处，使用反射在运行时把isMergeable强制改成false。
     */
    @Override
    public boolean sqlMapGenerated(GeneratedXmlFile sqlMap, IntrospectedTable introspectedTable) {
        try {
            java.lang.reflect.Field field = sqlMap.getClass().getDeclaredField("isMergeable");
            field.setAccessible(true);
            field.setBoolean(sqlMap, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }
}
